package jmind.core.aspect;

import java.util.List;
import java.util.StringTokenizer;
import java.util.concurrent.ConcurrentMap;

import jmind.core.annotation.CacheMonitor;
import jmind.core.cache.xmemcached.Memcached;
import jmind.core.geo.GLocation;
import jmind.core.manager.MemcachedManager;
import jmind.core.util.DataUtil;
import jmind.core.util.GlobalConstants;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.interceptor.CacheAspectSupport;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

/**
 * 基于SpEL 解析key 的缓存形式
 * spel #表示参数对象， @可以引用spring注解对象 #root表示跟对象，#this表示本身。还可以用 < > and or 等等
 * @CacheMonitor(name = "cacheName", prefix = "userid",spel="#user.cid,#name",exp=3600)
 * @CacheMonitor(name = "cacheName", spel = "'userid_'+#userId")
 * @author wbxie
 * @see CacheAspectSupport http://sishuok.com/forum/blogPost/list/2463.html
 * @see http://www.cnblogs.com/rollenholt/p/4202631.html
 * @see Cacheable
 * @see CacheEvict
 * 扫描表达式配置 http://www.cnblogs.com/leiOOlei/p/3613352.html
 * spring aop 这种实现方式有内部调用问题，即类本身调用aop的方法不会被aop 
 * 解决方案参考 http://blog.csdn.net/wxwzy738/article/details/27566645
1、增加配置
<aop:aspectj-autoproxy proxy-target-class="true" />
<aop:config expose-proxy="true"  />
 2、修改我们的业务实现类
this.b();-----------修改为--------->((AService) AopContext.currentProxy()).b();
 * 2013-12-4
 */
//@Component
@Aspect
public class CacheSpELAspect {

    final ExpressionParser parser = new SpelExpressionParser();
    final ConcurrentMap<String, List<Expression>> expressionMap = Maps.newConcurrentMap();

    //org.springframework.cache.annotation.Cacheable
    @Pointcut("@annotation(jmind.core.annotation.CacheMonitor)")
    public void exe2() {
    }

    @Around("exe2()")
    public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
        return doit(pjp);
    }

    private List<Expression> getExpressions(String spel) {
        List<Expression> list = expressionMap.get(spel);
        if (list == null) {
            list = Lists.newArrayList();
            StringTokenizer token = new StringTokenizer(spel, GlobalConstants.COMMA);
            while (token.hasMoreTokens()) {
                list.add(parser.parseExpression(token.nextToken()));
            }
            expressionMap.put(spel, list);
        }
        return list;

    }

    private Object doit(ProceedingJoinPoint pjp) throws Throwable {

        MethodSignature signature = (MethodSignature) pjp.getSignature();

        final CacheMonitor m = signature.getMethod().getAnnotation(CacheMonitor.class);
        //        System.out.println(Arrays.toString(signature.getParameterNames()));
        //        System.out.println(Arrays.toString(signature.getParameterTypes()));
        String key;
        if (m.spel().isEmpty()) {
            key = DataUtil.composeKey(m.prefix(), pjp.getArgs());
        } else {

            StandardEvaluationContext context = new StandardEvaluationContext();
            String[] names = signature.getParameterNames(); // 参数变量名
            Object[] values = pjp.getArgs(); // 参数值
            for (int i = 0; i < names.length; i++) {
                context.setVariable(names[i], values[i]);
            }

            StringBuilder sb = new StringBuilder(m.prefix());
            List<Expression> expressions = this.getExpressions(m.spel());
            for (Expression exp : expressions) {
                sb.append(GlobalConstants.HYPHEN).append(exp.getValue(context));
            }
            key = sb.toString();
        }

        Memcached cache = MemcachedManager.getInstance().getResource(m.name());
        //添加参数值 做为key
        if (m.remove()) {
            cache.delete(key);
            return pjp.proceed();
        }
        Object obj = cache.get(key);
        if (obj == null) {
            obj = pjp.proceed();
            if (obj != null)
                cache.set(key, m.exp(), obj);
        }
        return obj;
    }

    /**
     * SpEL 解析列子
     * org.springframework.cache.interceptor.ExpressionEvaluator
     * http://sishuok.com/forum/blogPost/list/2463.html
     * 2013-12-4
     */
    public static void expression() {
        ExpressionParser parser = new SpelExpressionParser();
        //1.给root对象赋值 
        GLocation root = new GLocation();
        root.setCity("湖南");
        root.setX(101);

        GLocation user = new GLocation();
        user.setCity("beijing");
        user.setX(12);
        StandardEvaluationContext context = new StandardEvaluationContext(root);
        context.setVariable("userId", 101);
        context.setVariable("user", user);
        Object value = parser.parseExpression("#userId + 'findById'").getValue(context);
        System.out.println(value);

        value = parser.parseExpression("#user.city").getValue(context);
        System.out.println(value);

        String result1 = parser.parseExpression("#root.city").getValue(context, String.class);
        System.out.println(result1);

        Object result = parser.parseExpression("#this").getValue(context);
        System.out.println(result);

        //        result1 = parser.parseExpression("x").getValue(context, Integer.class);
        //        System.out.println(result1);
        //        user.setCity("cs");
        //        result1 = parser.parseExpression("getX()").getValue(context, Integer.class);
        //        System.out.println(result1);

        //        try {
        //            String[] names = MethodSupport.getMethodParamNames(GLocation.class, "setAddress");
        //            System.out.println(Arrays.toString(names));
        //        } catch (Exception e) {
        //            // TODO Auto-generated catch block
        //            e.printStackTrace();
        //        }
    }

    public static void main(String[] args) {
        String s = "abc";
        long t = System.currentTimeMillis();
        StringBuilder sb = new StringBuilder();

        StringTokenizer token = new StringTokenizer(s, GlobalConstants.COMMA);
        while (token.hasMoreTokens()) {
            sb.append(token.nextToken());
        }
        System.out.println(System.currentTimeMillis() - t);
        System.out.println(sb);
        t = System.currentTimeMillis();
        sb = new StringBuilder();
        String[] a = s.split(",");
        for (String aa : a) {
            sb.append(aa);
        }

        System.out.println(System.currentTimeMillis() - t);
        System.out.println(sb);
    }

}
